<?php
# autoHTML: the dynamic PHP5 library for fast website prototyping
# http://code.google.com/p/autohtml/
#
# Copyright (c) 2010, Shu Zong Chen
# http://freelanceDreams.com/
# All rights reserved. 
# 
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted provided that the following conditions are met: 
# 
#  * Redistributions of source code must retain the above copyright notice, 
#    this list of conditions and the following disclaimer. 
#  * Redistributions in binary form must reproduce the above copyright 
#    notice, this list of conditions and the following disclaimer in the 
#    documentation and/or other materials provided with the distribution. 
#  * Neither the name of autoHTML nor the names of its contributors 
#    may be used to endorse or promote products derived from this software 
#    without specific prior written permission. 
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
# POSSIBILITY OF SUCH DAMAGE.

namespace fd\autoserver;
class CONNECTION{
	const IN_LIMBO = 0;
	const IN_HEADER = 1;
	const IN_BODY = 2;

	protected $done_reading = false;
	protected $done_writing = false;
	protected $dead = false;

	protected $mode = false;
	protected $headers = null;
	protected $buffer = '';
	protected $buffer_i = 0;

	function __construct($parent, $name, $socket){
		//echo "New connection!\n";
		$this->parent = $parent;
		$this->name = $name;
		$this->socket = $socket;
		$this->mode = self::IN_LIMBO;
		$this->alive = true;
		$this->headers = array();
		$this->write_queue = array();
	}
	function getSocket(){
		return $this->socket;
	}
	function getName(){
		return $this->name;
	}
	function listen(){
		$buf = socket_read($this->socket, 2048);
		if ($buf === false){
			$this->done_reading = true;
		}
		if (strlen($buf) == 0){
			$this->done_reading = true;
		}
		echo "\n:$buf\n";
		$this->buffer .= $buf;
	}

	function fetchNextBlock(){
		$a = strpos($this->buffer, "\n", $this->buffer_i);
		if ($a !== false){
			$ret = substr($this->buffer, $this->buffer_i, ($a - $this->buffer_i));
			$this->buffer_i = $a+1;
			return $ret;
		}
		return false;
	}

	function parse(){
		if (strlen($this->buffer) > 0){
			switch ($this->mode){
				case self::IN_LIMBO:
					if ($msg = $this->fetchNextBlock()){
						$this->header = HEADER_FACTORY::makeHeader($msg);
						$this->mode = self::IN_HEADER;
						$this->queueWrite($this->header->getInitialResponse());
					}
					break;
				case self::IN_HEADER:
					if ($msg = $this->fetchNextBlock()){
						$msg = rtrim($msg);
						if ($msg !== ''){
							$this->header->setNewOption($msg);
						}else{
							$this->mode = self::IN_BODY;
							$r = $this->header->hasResponse();
							if ($r === false){
								$r = $this->parent->getSite()->makeResponse($this->header);
							}
							foreach ($r->getQueue() as $line){
								$this->queueWrite($line);
							}
						}
					}
					break;
				case self::IN_BODY;
					$this->done_reading = true;

					# we will treat the connection as dead here.
					$this->dead = true;
					break;
			}
		}
	}
	function close(){
		echo "Socket closing!\n";
		try{
			socket_shutdown($this->socket, 1);
			socket_shutdown($this->socket, 0);
			socket_close($this->socket);
		}catch (\Exception $e){
		}
		return true;
	}
	function queueWrite($msg){
		if ($msg !== null){
			$this->done_writing = false;
			$this->write_queue[] = $msg;
		}
	}
	function respond(){
		if (sizeof($this->write_queue) > 0){
			$msg = array_shift($this->write_queue);
			$r = @socket_write($this->socket, $msg, strlen($msg));
			#socket_send($this->socket, $talkback, strlen($talkback), MSG_EOF);
			#echo ':', addcslashes($msg, "\0..\37!@\177..\377"), PHP_EOL;
		}
		if (sizeof($this->write_queue) == 0){
			$this->done_writing = true;
		}
	}
	function isDead(){
		return ($this->done_reading && $this->done_writing && $this->dead);
	}
	function hasQueue(){
		return (sizeof($this->write_queue) > 0);
	}
}